;;; ----------------------------------------------------------------------
;;; concept code for controlling an (8-bit mode) IDE interface
;;; through a 6522 VIA.
;;; --
;;; Chris Baird,, <cjb@brushtail.apana.org.au>

;;; ----------------------------------------------------------------------
;;; ATA IDE drive stuff

	    IDE_REG_data        = 0
	    IDE_REG_error       = 1 ; when reading
	    IDE_REG_count       = 2 ; number of sectors to read/write
	    IDE_REG_LBA0        = 3
	    IDE_REG_LBA8        = 4
	    IDE_REG_LBA16       = 5
	    IDE_REG_LBA24       = 6 ; b4 = drive#=0, b6=LBA=1, b5+7=1
	    IDE_REG_sector      = 3
	    IDE_REG_cylinderlo  = 4
	    IDE_REG_cylinderhi  = 5
	    IDE_REG_head        = 6 ; b4 = drive#, b6=LBA=0
	    IDE_REG_status      = 7 ; when reading
	    IDE_REG_command     = 7 ; when writing

	    IDE_head_drive      = 16 ; various bit fields
	    IDE_head_LBA        = 64
	    IDE_head_extras     = 128 + 32 ; required MFM codes
	    IDE_status_Busy     = 128
	    IDE_status_Ready    = 64
	    IDE_status_DataReq  = 8
	    IDE_status_Err      = 1

	    IDE_DCR_SRST                = $04

	    IDE_CMD_IdentifyDrive       = $EC
	    IDE_CMD_Idle                = $95
	    IDE_CMD_InitDriveParams     = $91
	    IDE_CMD_ReadSector          = $20
	    IDE_CMD_Seek                = $70
	    IDE_CMD_Recalibrate         = $10
	    IDE_CMD_SetFeatures         = $EF
	    IDE_CMD_WriteSector         = $30

;;; ----------------------------------------------------------------------

	    VIA2 	= $9800
	    PORTB	= VIA2		; PORTB = to IDE D0-D7
	    DDRB	= VIA2 + 2
	    PORTA 	= VIA2 + 15	; PORTA = to IDE control lines
	    DDRA 	= VIA2 + 3

	    b_RD	= %00001000
	    b_WR	= %00010000
	    b_CS0	= %00100000
            b_RST       = %01000000

            IADDRS      = $FB
            ZT1         = $FD

            Sector_Buffer = $1000

;;; ----------------------------------------------------------------------

	    .org $A000

            lda #147
            jsr $FFD2
            lda #0
            tay
s0:         sta 37888,y
            sta 38138,y
            iny
            bne s0

	    jsr Disk_Init

            ;;

loop:
            lda #<Sector_Buffer
	    sta IADDRS
	    lda #>Sector_Buffer
	    sta IADDRS+1

            ldy IDE_LBA0
            sty 4580
            ldy IDE_LBA8
            sty 4581
            ldy IDE_LBA16
            sty 4582
            ldy IDE_LBA24
            sty 4583

	    jsr ReadSector

            clc
            lda IDE_LBA0
            adc #1
            sta IDE_LBA0
            lda IDE_LBA8
            adc #0
            sta IDE_LBA8
            lda IDE_LBA16
            adc #0
            sta IDE_LBA16
            lda IDE_LBA24
            adc #0
            sta IDE_LBA24

            jmp loop

IDE_LBA0:   .byte 0
IDE_LBA8:   .byte 0
IDE_LBA16:  .byte 0
IDE_LBA24:  .byte 0

;;; ----------------------------------------------------------------------
;;; ----------------------------------------------------------------------

Disk_Init:
	    lda #$FF		; port A is all outputs
	    sta DDRA		; B is taken care of later
	    lda #b_WR + b_RD + b_CS0 ; reset the drive as well
	    sta PORTA
            jsr resetdelay
	    lda #b_WR + b_RD + b_CS0 + b_RST
            sta PORTA

            lda #0              ; set these in case a routine
            sta IDE_LBA0        ; doesn't want to specify the
            sta IDE_LBA8        ; whole range
            sta IDE_LBA16
            sta IDE_LBA24

            lda #IDE_CMD_InitDriveParams
            ldx #IDE_REG_command
            jmp IDE_Write_Register

;;;

resetdelay:
            ldx #10
            ldy #0
sdl1:
            dey
            bne sdl1
            dex
            bne sdl1
            rts

;;;

IDE_Write_Register:
	    ;; register to write in X
	    ;; value taken from A

	    pha
	    lda #$FF		; set PORTB for output
	    sta DDRB

	    ;; /CS0=0; /RD=1; /WR=1; /RESET=1; A0:2
	    txa
	    ora #(b_RD + b_WR + b_RST) ; activate CS0
	    sta PORTA

	    tax
	    pla
	    sta PORTB ;the byte to go.. (does it matter if /CS0 is first?)
	    txa

	    and #~b_WR		;activate WR
	    sta PORTA

	    ora #b_WR		;deactivate WR
	    sta PORTA

	    ora #b_CS0		;deactivate CS0
	    sta PORTA

	    rts

;;;

IDE_Read_Register:
	    ;; register to read in X
	    ;; value returned in A

	    lda #0		; set PORTB for input
	    sta DDRB

	    ;; /CS0=0; /RD=1; /WR=1, A0:2
	    txa
	    ora #(b_RD + b_WR + b_RST) ; activate /CS0
	    sta PORTA

	    and #~b_RD		; activate /RD
	    sta PORTA

	    ldx PORTB

	    ora #b_RD
	    sta PORTA		; deactivate /RD

	    ora #b_CS0		; deactive /CS0
	    sta PORTA
	    txa

            rts

;;;

IDE_Send_LBA:
            jsr IDE_WaitGood

            lda IDE_LBA0
            ldx #IDE_REG_LBA0
            jsr IDE_Write_Register

            lda IDE_LBA8
            ldx #IDE_REG_LBA8
            jsr IDE_Write_Register

            lda IDE_LBA16
            ldx #IDE_REG_LBA16
            jsr IDE_Write_Register

            lda IDE_LBA24
            and #15
            ora #IDE_head_LBA + IDE_head_extras
            ldx #IDE_REG_LBA24
            jsr IDE_Write_Register

            lda #1                  ; one sector at a time..
            ldx #IDE_REG_count
            jmp IDE_Write_Register

;;;

ReadSector:
	    jsr IDE_Send_LBA

	    lda #IDE_CMD_ReadSector
	    ldx #IDE_REG_command
	    jsr IDE_Write_Register

	    jsr IDE_WaitReady
	    jsr IDE_WaitDataReq

	    ;jsr IDE_CheckError	; check error flags
	    ;bcs TheRts		; :/

	    ldy #0
rs1:
            ldx #IDE_REG_data
	    jsr IDE_Read_Register
	    sta (IADDRS),y
	    iny
	    bne rs1
	    jmp IDE_WaitGood

;;;

WriteSector:
	    jsr IDE_Send_LBA

	    lda #IDE_CMD_WriteSector
	    ldx #IDE_REG_command
	    jsr IDE_Write_Register

	    jsr IDE_WaitnotBusy
	    jsr IDE_WaitDataReq

	    ldy #0
w_s1:
            lda (IADDRS),y
	    ldx #IDE_REG_data
	    jsr IDE_Write_Register
	    iny
	    bne w_s1
	    jsr IDE_WaitGood

	    jmp IDE_CheckError	; check (and return) error flags

;;; these routines are probably a good place to do sanity/error checking..
;;; involving timeouts most likely..

IDE_WaitGood:
	    jsr IDE_WaitnotBusy

            ;; wait-to-go-high

IDE_WaitReady:
	    lda #IDE_status_Ready
	    .byte $2c
IDE_WaitDataReq:
	    lda #IDE_status_DataReq
            .byte $2c
IDE_CheckError:
            lda #IDE_status_Err

	    sta ZT1
I_WSH1:
            ldx #IDE_REG_status
	    jsr IDE_Read_Register
	    bit ZT1
	    beq I_WSH1		; TODO delay and timeout code here
 	    rts

            ;; wait-to-go-low

IDE_WaitnotBusy:
            lda #IDE_status_Busy
	    sta ZT1
I_WSH2:
            ldx #IDE_REG_status
	    jsr IDE_Read_Register
	    bit ZT1
	    bne I_WSH2		; TODO delay and timeout code here
            ;; is carry clear?
 	    rts

;;; ----------------------------------------------------------------------
